#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/time.h>

#include "utils.h"

#ifndef DEBUG
#define DEBUG 0
#endif

#ifndef MY_DATA_TYPE
#define MY_DATA_TYPE float
#endif

#define MEASURE 1

void usage(void)
{
	printf("Syntax Error!\n");
	printf("Usage: ./mvmult <random seed> <matrix size>\n");
}

int main(int argc, char *argv[])
{
	/* check programm call */
	if (3 != argc)
	{
		usage();
		exit(-1);
	}
	
	const int SEED = atoi(argv[1]);
	const int SIZE = atoi(argv[2]);

	unsigned int i,j;
	unsigned long runtime, flops;
	#if MEASURE == 1
	struct timeval start, end, diff;
	#else
	unsigned long long tsc[2];
	#endif
	
	/* allocate memory */
	MY_DATA_TYPE *vector, *result, **matrix;
	vector = (MY_DATA_TYPE*) malloc(SIZE * sizeof(MY_DATA_TYPE));
	result = (MY_DATA_TYPE*) malloc(SIZE * sizeof(MY_DATA_TYPE));
	matrix = (MY_DATA_TYPE**) malloc(SIZE * sizeof(MY_DATA_TYPE*));
	for (i=0; i<SIZE; i++)
	{
		matrix[i] = (MY_DATA_TYPE*) malloc(SIZE * sizeof(MY_DATA_TYPE));
	}
	#if DEBUG == 1
	// RAM = sizeof(dtype) * (V+R+M^2)
	printf("allocated RAM: %.6lf MByte \n",
		(double)(sizeof(MY_DATA_TYPE)*(SIZE+SIZE+SIZE*SIZE))/1e6);
	#endif
		
	/* init arrays */
	srand(SEED);
	for (i=0; i<SIZE; i++)
	{
		result[i] = 0;
		#if DEBUG == 1
		vector[i] = rand() % 10;
		printf("v[%i] = %f \n", i, vector[i]);
		#else
		vector[i] = (MY_DATA_TYPE)(rand() / 10000.0f);
		#endif
	}
	#if DEBUG == 1
	printf("\n");
	#endif
	for (i=0; i<SIZE; i++)
	{
		for (j=0; j<SIZE; j++)
		{
			#if DEBUG == 1
			matrix[i][j] = rand() % 10;
			printf("m[%i][%i] = %f ", i, j, matrix[i][j]);
			#else
			matrix[i][j] = (MY_DATA_TYPE)(rand() / 10000.0f);
			#endif
		}
		#if DEBUG == 1
		printf("\n");
		#endif
	}
	#if DEBUG == 1
	printf("\n");
	#endif

	/* matrix-vector multiplication */
	#if MEASURE == 1
	gettimeofday(&start, NULL);
	#else
	rdtsc(tsc[0]);
	#endif
	for (i=0; i<SIZE; i++)
	{
		for (j=0; j<SIZE; j++)
		{
			result[i] += matrix[i][j] * vector[j];
		}
		#if DEBUG == 1
		printf("res[%i] = %f \n", i, result[i]);
		#endif
	}
	#if MEASURE == 1
	gettimeofday(&end, NULL);
	#else
	rdtsc(tsc[1]);
	#endif

	/* calc time difference */
	#if MEASURE == 1
	diff.tv_sec = end.tv_sec - start.tv_sec;
	if (start.tv_usec > end.tv_usec)
	{
		diff.tv_usec = (1e6 - end.tv_usec) + start.tv_usec;
	}
	else
	{
		diff.tv_usec = end.tv_usec - start.tv_usec;
	}
	runtime = diff.tv_sec * 1e6 + diff.tv_usec; // [us]
	#else
	runtime = tsc[1]-tsc[0]; // Zeitbasis?!
	#endif

	flops = (SIZE*SIZE*2)/runtime; //2: je durchlauf 1*multiply+1*add

	#if DEBUG == 1
	printf("runtime=%ld us\n", runtime);
	printf("flops=%ld mflop/s\n", flops);
	#endif
	
	/* clean up */
	free(vector);
	free(matrix);
	free(result);
	
	/* gnuplot data */
	printf("# Size runtime[us] flops[M/s]\n");
	printf("%i %ld %ld\n", SIZE, runtime, flops);
	
	return 0;
}

